"""
The front end of the POP3 protocol using the Twisted POP3 client
Written By: Daniel Ortiz
Date: November 9 2007
Version: 0.0
"""

from twisted.internet import defer, protocol
from twisted.mail import pop3client
from database.mapclass import *
import email

class POP3Client(pop3client.POP3Client):

    def serverGreeting(self, greeting):
        """Function handles the initial connection to the server
        pop3client.POP3Client.serverGreeting(self, greeting)"""
        if not self.factory.check:
            pop3client.POP3Client.serverGreeting(self, greeting)
            login = self.login(self.factory.username, self.factory.password).addErrback(self.PrintError)
            login.addCallback(self.loggedIn).addErrback(self.PrintError)
        else:
            self.factory.f(True)
            self.quit()

    def PrintError(self, error):
        print error.getErrorMessage()

    @defer.deferredGenerator
    def Synchronize(self, *args, **kwargs):
        """Synchronize the server and the client database"""
        d = self.noop()
        wfd = defer.waitForDeferred(d)
        yield wfd
        data = wfd.getResult()
        d = self.listUID()
        wfd = defer.waitForDeferred(d)
        yield wfd
        uids = wfd.getResult()
        self.factory.handler.SetSynchGauge(uids, new = not self.factory.sync)
        for i in range(self.factory.m.size, len(uids), 1):
            d = self.retrieve(i)
            wfd = defer.waitForDeferred(d)
            yield wfd
            line = wfd.getResult()
            parsedMessage = email.message_from_string('\r\n'.join(line))
            self.factory.handler.IncGauge()
            m = create_Mail_from_email(parsedMessage, self.factory.m, i)
            self.factory.handler.SaveMail(m, self.factory.m)
            self.factory.new_m.size += 1
        self.factory.f(old_m = self.factory.m, new_m = self.factory.new_m)
            
    def loggedIn(self, result):
        """Get messages from the server"""
        return self.Synchronize()
#        print "Synch", result
#        d = self.listUID().addCallback(self.handleUID)
#        return d.addCallback(lambda _: self.listSize().addCallback(self.getAllMessages))
    
    def handleUID(self, data):
        """Save the uids of the mailbox"""
        self.mUIDs = data
        self.factory.handler.SetSynchGauge(self.mUIDs, new = not self.factory.sync)
        return defer.succeed(True)

    @defer.deferredGenerator
    def getAllMessages(self, size):
        """Get all of the messages from the server"""
        length = len(size)
        tsize = self.factory.m.size
        self.factory.m.size = 0
        print "Length", len(self.mUIDs)
        for i in range(tsize, length, 1):
            retr.append(self.retrieve(i).addCallback(self.gotMessageLine, index = i))
        return defer.DeferredList(retr).addCallback(lambda _: self.factory.f(old_m = self.factory.old_m, new_m = self.factory.m))
    
    def uid_delete(self, index):
        """Overwritten delete for the pop3client"""
        if isinstance(index, basestring):
            return self.sendShort('DELE', str(index))
        else:
            return self.sendShort('DELE', str(index + 1))

    def gotMessageLine(self, line, **kw):
        message = '\r\n'.join(line)
        parsedMessage = email.message_from_string(message)
        self.factory.m.size += 1
        print "Size", self.factory.m.size, self.factory.old_m.size
        self.factory.handler.IncGauge()
        m = create_Mail_from_email(parsedMessage, self.factory.old_m, kw['index'], message)
        self.factory.handler.SaveMail(m, self.factory.old_m, no_save = True)

    def finish(self, t):
        return self.quit()

class POP3Factory(protocol.ClientFactory):
    protocol = POP3Client

    def __init__(self, username, password, f,  m, check = False, h = None):
        """Constructor takes in username and password, and a callback"""
        self.username = username
        self.password = password
        self.f = f
        self.m = m
        self.new_m = Mailbox(*m.get()[:])
        self.check = check
        self.handler = h
        self.sync = False

    def buildProtocol(self, addr):
        """Save a reference to the protocol"""
        self.proto = self.protocol()
        self.proto.factory = self
        return self.proto

    def clientConnectionFailed(self, connector, err):
        """Parse a connection error if in the CHECK state"""
        if self.check:
            self.f(None, err)

    def clientConnectionLost(self, connector, err):
        """Handle a lost connection by ignoring it"""
        pass
    
    def DeleteMail(self, index, f):
        """Delete the mail object corresponding to the index"""
        return self.proto.delete(index).addCallback(f)

if __name__ == '__main__':
    def stopreactor(val):
        print 'Here'
    import getpass
    from structures import google
    from twisted.internet import reactor, ssl
    username = raw_input('Username:')
    password = getpass.getpass('Password:')
    f = POP3Factory(username, password, stopreactor)
    reactor.connectSSL(google['iserver'], google['iport'], f,
                       ssl.ClientContextFactory())
    reactor.run()
    
